package com.lucas.edu.servlet;

import com.lucas.edu.annotation.*;
import com.lucas.edu.pojo.Handler;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * url唯一入口，所有对系统的访问都会到这个地方里面。
 */
public class dispatcherServlert extends HttpServlet {
    //存放url的映射关系，需要放在
    static private Map<String, Handler> handlerMapping = new HashMap<String , Handler>();

    private static Properties properties = new Properties();
    //ioc 容器
    private  Map<String,Object> ioc = new HashMap<>();

    //扫描到全限定类名
    private List<String> classNames = new ArrayList<>();


    //初始化方法
    @Override
    public void init(ServletConfig config){
        /**
         * 可断点debug查看
         */
        System.out.println("initParameter "+config.getInitParameter("contextConfigLocation"));//在web.xml配置了
        System.out.println("servlerContext "+config.getServletContext());
        Enumeration<String> initParameterNames = config.getInitParameterNames();
        String servletName = config.getServletName();

        //1.读取配置到流（通过注解实现） //classpath:springmvc.properties
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        //2.获取流的信息
        readConfig(contextConfigLocation);
        //3.使用获取信息，封装数据
        doScan(properties.get("scanPackage").toString());
        //4.初始化
        doInstance();
        //5.构建依赖
        doAutowired();
        //6.构建url与方法的依赖
        initHnandlerMapping();

        //7.构建安全认证
        doSecurity();

        System.out.println("springmvc初始化完成");

    }

    private void doSecurity() {
        if(ioc.isEmpty())return;
        if(handlerMapping.isEmpty())return;

        for (Map.Entry<String, Handler> stringHandlerEntry : handlerMapping.entrySet()) {
            String beanName = stringHandlerEntry.getValue().getBeanName();
            //检查对应的类
            Object o = ioc.get(beanName);
            //如果类中没有security就过滤
            if(!o.getClass().isAnnotationPresent(Security.class))continue;
            String[] ClassSercurityAnnotation = o.getClass().getAnnotation(Security.class).value();
            stringHandlerEntry.getValue().setSecurityList(Arrays.asList(ClassSercurityAnnotation));

            //检查对应的方法
            if(stringHandlerEntry.getValue().getMethod().isAnnotationPresent(Security.class)){
                String[] methodSecurityAnnoataion = stringHandlerEntry.getValue().getMethod().getAnnotation(Security.class).value();
                stringHandlerEntry.getValue().setSecurityList(Arrays.asList(methodSecurityAnnoataion));
            }

        }
    }

    private void doAutowired()  {
        if(ioc.isEmpty())return;
        try {
            //autowired 的处理
            for (Map.Entry<String, Object> entry : ioc.entrySet()) {
                //controller
                Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
                //遍历类下面的所有字段 ，找到带有autowired
                for (Field declaredField : declaredFields) {
                    declaredField.setAccessible(true);
                    //service
                    if (!declaredField.isAnnotationPresent(Autowired.class)) continue;

                    //获取字段的名字
                    String beanName = declaredField.getAnnotation(Autowired.class).value();
                    Object targetObj = null;
                    if (beanName.equals("")) {
                        String s = lowerFirst(declaredField.getType().getSimpleName());
                        targetObj = ioc.get(s);
                    } else {
                        targetObj = ioc.get(beanName);
                    }
                    declaredField.set(entry.getValue(), targetObj);

                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 根据names 实例化数据
     */
    @SuppressWarnings("checkstyle:WhitespaceAround")
    private void doInstance() {
        if (classNames.isEmpty()) return;
        try {
            //ioc 的controller 以及 service 导入
            for(String name :classNames){
                Class<?> aClass = Class.forName(name);
                //把对应标记了要加入的ioc的，即controller或者service都加入
                if(aClass.isAnnotationPresent(Controller.class)){

                    getClassByAnnotation(Controller.class,aClass);

                }else if(aClass.isAnnotationPresent(Service.class)){

                    getClassByAnnotation(Service.class,aClass);

                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void getClassByAnnotation(Class<? extends Annotation> annotationClass,Class<?> clazz){
        try {
                if (clazz.isAnnotationPresent(annotationClass)) {
                    Annotation annotation = clazz.getAnnotation(annotationClass);
                    Method valueMethod = annotation.getClass().getMethod("value");
                    Object result = valueMethod.invoke(annotation, null);

                    if (!result.equals("")&&!result.equals(null)) {
                        ioc.put(result.toString(), clazz.newInstance());
                    } else {
                        String lowerFirst = lowerFirst(clazz.getSimpleName());
                        ioc.put(lowerFirst, clazz.newInstance());
                    }
                }
            } catch(InstantiationException e){
                e.printStackTrace();
            } catch(IllegalAccessException e){
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    //把含有requestMapping的找到然后封装起来
    private void initHnandlerMapping() {
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> aClass = entry.getValue().getClass();
            if(!aClass.isAnnotationPresent(RequestMapping.class))continue;

            String baseUrl = aClass.getAnnotation(RequestMapping.class).value();
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {

                 if(!method.isAnnotationPresent(RequestMapping.class))continue;
                String subUrl = method.getAnnotation(RequestMapping.class).value();
                String url = (baseUrl+subUrl).replace("/+","/");//利用正则表达式
                Handler handler = new Handler(url,method,entry.getKey());
                Parameter[] parameters = method.getParameters();
                Map<String,Integer> map = new HashMap<>();
                int index=0;
                for(Parameter item :parameters){
                    String name = item.getName();
                    map.put(name,index++);
                }
                handler.setParamMap(map);
                handlerMapping.put(url,handler);
            }


        }

    }

    private void doScan(String scanpackage)  {
        //d:/code/file/
        String path =Thread.currentThread().getContextClassLoader().getResource("").getPath()+scanpackage.replace(".","/");

        File file = new File(path);

        File[] files = file.listFiles();
        for (File item : files) {
            if(item.isDirectory()){
                doScan(scanpackage+"."+item.getName());
            }else {
                String name = item.getName().replace(".class", "");
                String className = scanpackage+"."+ name;
                classNames.add(className);
            }
        }


    }

    private void parse(Map<String, Object> configInfo) {
        configInfo.get("");

    }

    /**
     * 读取配置文件
     * @param contextConfigLocation 配置文件路径
     */
    private void readConfig(String contextConfigLocation) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    // 首字母小写方法
    public String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if('A' <= chars[0] && chars[0] <= 'Z') {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURL = req.getRequestURI();
        Handler handler = handlerMapping.get(requestURL);
        String beanName = handler.getBeanName();
        Method method = handler.getMethod();
        Object targetObj = ioc.get(beanName);
        Object[] params = new Object[req.getParameterMap().size()];
        Map<String, String[]> parameterMap = req.getParameterMap();
        for (Map.Entry<String, String[]> stringEntry : parameterMap.entrySet()) {
            Integer indexParam = handler.getParamMap().get(stringEntry.getKey());
            String value = StringUtils.join(stringEntry.getValue(), ",");
            params[indexParam]=value;
        }
        try {
                resp.setHeader("Content-type", "text/html;charset=UTF-8");
//                resp.setCharacterEncoding("UTF-8");
            if(!doCheckSecurity(handler,parameterMap)){
                method.invoke(targetObj,params);
                resp.getOutputStream().write("方法有权限访问".getBytes("UTF-8"));
            }else {
                System.out.println("方法没有权限访问");
                resp.getOutputStream().write("方法没有权限访问".getBytes("UTF-8"));
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }


    }

    private boolean doCheckSecurity(Handler handler, Map<String, String[]> parameterMap) {
        List<String> securityList = handler.getSecurityList();//获取该handler的验证限制。
        String[] names = parameterMap.get("name");
        for (String name : names) {
            if(securityList.contains(name)) return true;
        }
        return false;
    }
}
